package com.susallwave.srcb.serviceimpl.sys;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.susallwave.srcb.constant.BaseConstant;
import com.susallwave.srcb.entity.sys.User;
import com.susallwave.srcb.exception.BusinessException;
import com.susallwave.srcb.mapper.sys.RolePermissionMapper;
import com.susallwave.srcb.mapper.sys.UserMapper;
import com.susallwave.srcb.mapper.sys.UserRoleMapper;
import com.susallwave.srcb.response.Result;
import com.susallwave.srcb.response.ResultEnum;
import com.susallwave.srcb.service.sys.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author MyBatisPlusGenerator
 * @since 2025-07-19
 */
@Slf4j
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final Environment env;
    private final UserMapper userMapper;
    private final UserRoleMapper userRoleMapper;
    private final RolePermissionMapper rolePermissionMapper;
    private final RedisTemplate redisTemplate;

    @Override
    public Result login(String userAcct, String password) {

        // 查询用户存在性
        User user = userMapper.queryWithPwd(userAcct);
        if (user == null) return Result.error(ResultEnum.NO_USER);

        // 是否被锁定
        if (StpUtil.isDisable(userAcct)) {
            return Result.error("账户已锁定，请 " + StpUtil.getDisableTime(userAcct) + " 秒后再试,或找管理员解锁");
        }

        // 机构变更会把账户置为失效,所以有此校验
        if (BaseConstant.INVALID.equals(user.getLockFlag())) return Result.error(ResultEnum.BANK_USER_INVALID);

        // 使用userAcct作为登录标识
        StpUtil.login(user.getUserAcct());

//        // 缓存权限信息
//        List<String> permissions = getPermissions(user.getuserAcct());
//        StpUtil.getSession().set("permissions", permissions);

        return Result.success(StpUtil.getTokenValue());
    }

    private List<String> getPermissions(String userAcct) {
        // 获取用户角色编码
        List<String> roleCodes = userRoleMapper.selectRoleByUserAcct(userAcct);

        // 获取角色对应的权限编码
        return rolePermissionMapper.selectPermissionByRoleCodes(roleCodes);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String loginBySign(String sign, String userAcct, String timestamp,String pageCode,String sysFlag) {

        final long MAX_TIME_DIFF = TimeUnit.MINUTES.toMillis(10); // 10分钟有效期,最大时间窗20分钟

        // 1. 验证时间有效性
        long currentTime = System.currentTimeMillis();
        long timeDiff = Math.abs(currentTime - Long.parseLong(timestamp));

        if (timeDiff > MAX_TIME_DIFF) {
            throw new BusinessException(ResultEnum.OVER_TIME);
        }
        // 验签:使用AES算法加上面的盐,等于sign则证明来自信任系统
        String computedSign = null;
        try {
            computedSign = encrypt(userAcct,Long.parseLong(timestamp));
        } catch (Exception e) {
            log.error("loginByTokenUrl-error for userAcct={}, pageCode={}, error={}", userAcct, pageCode, e.getMessage());
        }
        if (!StringUtils.equals(computedSign,sign)){
            throw new BusinessException(ResultEnum.SIGN_NOT_MATCH);
        }

        // 是否被锁定
        if (StpUtil.isDisable(userAcct)) {
            throw new BusinessException("账户已锁定，请 " + StpUtil.getDisableTime(userAcct) + " 秒后再试,或找管理员解锁");
        }

        // 使用userAcct作为登录标识
        StpUtil.login(userAcct);
        StpUtil.getSession().set("sysFlag",sysFlag); // 来访系统标志
/*        // 缓存权限信息
        List<String> permissions = getPermissions(user.getuserAcct());
        StpUtil.getSession().set("permissions", permissions);*/

        List<String> validPageCodes = Arrays.asList("greenCompany", "greenProject", "greenUse", "greenPer", "greenInvestAssess", "greenEsgAssess");
        if (!validPageCodes.contains(pageCode)) {
            throw new BusinessException(ResultEnum.PAGE_NOT_MATCH);
        }
        String redirectUrl = getRedirect(pageCode);

        return "redirect:" + redirectUrl;
    }

    private String getRedirect(String pageCode) {
        String redirectUrl;// TODO 重定向
        switch (pageCode) {
            case "greenCompany":
                redirectUrl = "/greenCompany";//企业绿识别
                break;
            case "greenProject":
                redirectUrl = "/greenProject";//项目绿识别
                break;
            case "greenUse":
                redirectUrl = "/greenUse";//用途绿识别
                break;
            case "greenPer":
                redirectUrl = "/profile";//绿色营收占比
                break;
            case "greenInvestAssess":
                redirectUrl = "/greenInvestAssess";//绿色投资评估
                break;
            case "greenEsgAssess":
                redirectUrl = "/greenEsgAssess";//ESG评估
                break;
            default:
                redirectUrl = "/home"; //
                break;
        }
        return redirectUrl;
    }

    // 使用相同的密钥和加密方式对用户信息进行加密
    public String encrypt(String userId, long timestamp) throws Exception{
        // 共享盐 - 必须是A/B系统相同的值
        String salt = env.getProperty("other.systemSalt");
        String textToEncrypt = userId + timestamp;
        // 创建密钥
        SecretKeySpec secretKey = new SecretKeySpec(salt.getBytes(StandardCharsets.UTF_8), "AES");
        // 创建Cipher对象，指定加密算法
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        byte[] bytes = cipher.doFinal(textToEncrypt.getBytes());
        // 执行加密
        return Base64.getEncoder().encodeToString(bytes);
    }

    public static String encrypt2(String userId, long timestamp) throws Exception {
        // 共享盐 - 必须是A/B系统相同的值
        String salt = "TrustfulSystems!";
        String textToEncrypt = userId + timestamp;
        // 创建密钥
        SecretKeySpec secretKey = new SecretKeySpec(salt.getBytes(StandardCharsets.UTF_8), "AES");
        // 创建Cipher对象，指定加密算法
        Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        byte[] bytes = cipher.doFinal(textToEncrypt.getBytes());
        // 执行加密
        return Base64.getEncoder().encodeToString(bytes);
    }

    public static void main(String[] args) throws Exception {
        long l = System.currentTimeMillis();
        System.out.println(l);
        String encrypt = encrypt2("zcx",l);
        System.out.println(encrypt);
    }

    @Override
    public Result<Void> logout() {
        // 先检查用户是否登录
        if (!StpUtil.isLogin()) {
            return Result.error("用户未登录");
        }
        // 执行登出操作
        StpUtil.logout();
        return Result.success();
    }

    @Override
    public Result<Void> lockUser(String userAcct,long timeout) {
        StpUtil.logout(userAcct);
        StpUtil.disable(userAcct,timeout);
        return Result.success();
    }

    @Override
    public Result<Void> unlockUser(String userAcct) {
        StpUtil.untieDisable(userAcct);
        this.lambdaUpdate().set(User::getLockFlag,BaseConstant.ACTIVE).eq(User::getUserAcct, userAcct).update();
        return Result.success();
    }

    @Override
    public Result<User> getUserInfo(String userAcct) {
        User user = this.getOne(new LambdaQueryWrapper<User>().eq(User::getUserAcct, userAcct));
        return Result.success(user);
    }

    @Override
    public Result<IPage<User>> userList(int pageNum, int pageSize, String keyword) {
        Page<User> page = new Page<>(pageNum, pageSize);
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();

        // // 构建查询条件 多字段模糊查询
        wrapper.like(User::getUserAcct,keyword).or().
                like(User::getSubDeptId,keyword).or().
                like(User::getPhone,keyword);

        return Result.success(this.page(page, wrapper));
    }
}
